﻿using NUnit.Framework;

namespace ServiceStack.OrmLite.MySql.Tests.Expressions;

public class UnaryExpressionsTest : ExpressionsTestBase
{
    #region constants

    [Test]
    public void Can_select_unary_plus_constant_expression()
    {
        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == +12);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_minus_constant_expression()
    {
        var expected = new TestType()
        {
            IntColumn = -12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == -12);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_not_constant_expression()
    {
        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = false,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.BoolColumn == !true);

        Assert.IsNotNull(actual);
        Assert.Greater(actual.Count, 0);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_not_constant_expression2()
    {
        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = false,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => !q.BoolColumn);

        Assert.IsNotNull(actual);
        Assert.Greater(actual.Count, 0);
        CollectionAssert.Contains(actual, expected);
    }

    #endregion

    #region variables

    [Test]
    public void Can_select_unary_plus_variable_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        var intVal = +12;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == intVal);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_minus_variable_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        var intVal = -12;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = -12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == intVal);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_not_variable_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        var boolVal = true;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = false,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.BoolColumn == !boolVal);

        Assert.IsNotNull(actual);
        Assert.Greater(actual.Count, 0);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_cast_variable_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        object intVal = 12;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == (int)intVal);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_cast_variable_with_explicit_userdefined_type_conversion_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        var intVal = 12;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var value = new IntWrapper(intVal);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == (int)value);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_cast_variable_with_implicit_userdefined_type_conversion_expression()
    {
        // ReSharper disable ConvertToConstant.Local
        var intVal = 12;
        // ReSharper restore ConvertToConstant.Local

        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var value = new IntWrapper(intVal);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == value);

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    #endregion

    #region method

    [Test]
    public void Can_select_unary_not_method_expression()
    {
        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = false,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.BoolColumn == !GetValue(true));

        Assert.IsNotNull(actual);
        Assert.Greater(actual.Count, 0);
        CollectionAssert.Contains(actual, expected);
    }

    [Test]
    public void Can_select_unary_cast_method_expression()
    {
        var expected = new TestType()
        {
            IntColumn = 12,
            BoolColumn = true,
            StringColumn = "test"
        };

        EstablishContext(10, expected);

        var actual = OpenDbConnection().Select<TestType>(q => q.IntColumn == (int)GetValue((object)12));

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count);
        CollectionAssert.Contains(actual, expected);
    }

    #endregion
}

public struct IntWrapper
{
    private readonly int _id;

    public IntWrapper(int projectId)
    {
        _id = projectId;
    }

    public static explicit operator IntWrapper(int value)
    {
        return new IntWrapper(value);
    }

    public static implicit operator int(IntWrapper value)
    {
        return value._id;
    }
}